﻿using Ofs.Shared;
using System.IO.Compression;
using System.Text.Json;

namespace Ofs
{
    public static class Program
    {
        public static async Task Main()
        {
            Console.WriteLine("要完成离线文件同步，需要依次进行以下步骤：");
            Console.WriteLine("1. 扫描目标计算机当前文件结构，生成目标计算机目录结构文件");
            Console.WriteLine("2. 扫描源计算机当前文件结构，生成源计算机目录结构文件");
            Console.WriteLine("3. 比对两者文件结构，从源计算机提取文件，生成更新包");
            Console.WriteLine("4. 在目标计算机加载更新包");
            Console.Write("请选择当前要执行的步骤（1/2/3/4）：");

            var line = Console.ReadLine()?.Trim();
            switch (line)
            {
                case "1":
                    await ScanAction();
                    break;
                case "2":
                    await ScanAction();
                    break;
                case "3":
                    await CompareAction();
                    break;
                case "4":
                    await LoadAction();
                    break;
                default:
                    Console.WriteLine("输入的内容有误，请重新运行程序并正确输入。");
                    break;
            }

            Console.WriteLine("程序已停止运行，请按空格键退出。");
            _ = Console.ReadKey();
        }

        private static ValueTask<ScanResult> ScanAsync(string? directoryPath)
        {
            var directory = new DirectoryInfo(directoryPath!);
            var scanner = new DirectoryScanner();
            scanner.NewFileScanning += (_, e) => {
                Console.WriteLine($"正在扫描 {e.ScanningFile?.FullName}");
            };
            return scanner.ScanAsync(directory);
        }

        private static FileInfo SelectFileToSave(string prompt)
        {
            Console.Write(prompt);
            var resultPath = Console.ReadLine();
            FileInfo file = new FileInfo(resultPath!);
            if (file.Exists)
            {
                Console.Write("文件已存在，请选择是否覆盖(y/n)：");
                var line = Console.ReadLine()?.Trim();
                if (line == "n")
                    return SelectFileToSave(prompt);
                else if (line != "y")
                {
                    Console.WriteLine("输入的内容有误，视为选择不覆盖。");
                    return SelectFileToSave(prompt);
                }
            }
            return file;
        }

        private static FileInfo SelectFileToOpen(string prompt)
        {
            Console.Write(prompt);
            var resultPath = Console.ReadLine();
            FileInfo file = new FileInfo(resultPath!);
            if (!file.Exists)
            {
                Console.Write("文件不存在，请重新选择。");
                return SelectFileToOpen(prompt);
            }
            return file;
        }
        private static DirectoryInfo SelectDirectoryToSave(string prompt)
        {
            Console.Write(prompt);
            var resultPath = Console.ReadLine();
            DirectoryInfo directory = new DirectoryInfo(resultPath!);
            if (directory.Exists &&
                (directory.EnumerateFiles().Any() || directory.EnumerateDirectories().Any()))
            {
                Console.Write("文件夹已存在且包含文件，请选择一个空文件夹。");
                return SelectDirectoryToSave(prompt);
            }
            return directory;
        }

        public async static ValueTask ScanAction()
        {
            Console.Write("请输入要扫描的文件夹路径：");
            var directoryPath = Console.ReadLine();

            var result = await ScanAsync(directoryPath);
            Console.WriteLine("生成成功。");
            var file = SelectFileToSave("请输入要保存到的位置：");

            using var s = file.Open(FileMode.Create);
            await JsonSerializer.SerializeAsync(s, result, ScanResultContext.Default.ScanResult);
            Console.WriteLine($"已成功将目录结构文件储存到 {file.FullName}");
        }

        public static async ValueTask CompareAction()
        {
            var scanResultFileOfTarget = SelectFileToOpen("请输入目标计算机目录结构文件的位置：");
            ScanResult? scanResultOfTarget;
            {
                using var s = scanResultFileOfTarget.OpenRead();
                scanResultOfTarget = await JsonSerializer.DeserializeAsync(
                    s, ScanResultContext.Default.ScanResult);
                Console.WriteLine($"已成功读取。");
            }

            var scanResultFileOfOrigin = SelectFileToOpen("请输入源计算机目录结构文件的位置：");
            ScanResult? scanResultOfOrigin;
            {
                using var s = scanResultFileOfOrigin.OpenRead();
                scanResultOfOrigin = await JsonSerializer.DeserializeAsync(
                    s, ScanResultContext.Default.ScanResult);
                Console.WriteLine($"已成功读取。");
            }

            {
                var file = SelectFileToSave("请输入储存更新包的位置：");
                using var s = file.Open(FileMode.Create);
                using var archive = new ZipArchive(s, ZipArchiveMode.Create);

                foreach (var (hash, path) in scanResultOfOrigin!.FileHashToFilePath)
                {
                    if (!scanResultOfTarget!.FileHashToFilePath.ContainsKey(hash))
                    {
                        Console.WriteLine($"正在添加 {path[0]}");
                        _ = archive.CreateEntryFromFile(path[0], hash);
                    }
                }

                _ = archive.CreateEntryFromFile(scanResultFileOfOrigin.FullName, "origin");
                _ = archive.CreateEntryFromFile(scanResultFileOfTarget.FullName, "target");

                Console.WriteLine($"已成功生成更新包到 {file.FullName}");
            }
        }

        public static async ValueTask LoadAction()
        {
            var archivePath = SelectFileToOpen("请输入更新包的位置：");
            using var archiveStream = archivePath.Open(FileMode.Open);
            using var archive = new ZipArchive(archiveStream, ZipArchiveMode.Read);

            ScanResult? scanResultOfTarget;
            {
                using var s = archive.GetEntry("target")!.Open();
                scanResultOfTarget = await JsonSerializer.DeserializeAsync(
                    s, ScanResultContext.Default.ScanResult);
                Console.WriteLine($"已成功读取目标计算机目录结构。");
            }
            ScanResult? scanResultOfOrigin;
            {
                using var s = archive.GetEntry("origin")!.Open();
                scanResultOfOrigin = await JsonSerializer.DeserializeAsync(
                    s, ScanResultContext.Default.ScanResult);
                Console.WriteLine($"已成功读取源计算机目录结构。");
            }

            var targetDirectory = SelectDirectoryToSave("请输入目标文件夹位置：");
            var originRoot = scanResultOfOrigin!.RootPath;
            var targetRoot = targetDirectory.FullName;
            foreach (var (hash, paths) in scanResultOfOrigin.FileHashToFilePath)
            {
                var contains = scanResultOfTarget!.FileHashToFilePath.ContainsKey(hash);
                foreach (var path in paths)
                {
                    var relativePath = Path.GetRelativePath(originRoot, path);
                    var targetPath = Path.GetFullPath(relativePath, targetRoot);
                    var targetFile = new FileInfo(targetPath);
                    targetFile.Directory!.Create();
                    Console.WriteLine($"正在复制 {relativePath}");
                    if (contains)
                    {
                        File.Copy(scanResultOfTarget.FileHashToFilePath[hash][0], targetPath);
                    }
                    else
                    {
                        using var s = archive.GetEntry(hash)!.Open();
                        using var ts = File.Open(targetPath, FileMode.CreateNew);
                        await s.CopyToAsync(ts);
                    }
                }
            }
            Console.WriteLine($"已成功生成到 {targetRoot}");
        }
    }
}